﻿using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using Microsoft.Win32;
using MVVMFoundation;

namespace DictationTool.ViewModel
{
    class MediaPlayerViewModel : ObservableObject
    {
        #region ctor

        public MediaPlayerViewModel()
        {
            mediaElement.LoadedBehavior = MediaState.Manual;
            mediaElement.MediaOpened += HandleMediaOpened;
            mediaElement.MediaEnded += HandleMediaEnded;
            mediaElement.MediaFailed += HandleMediaFailed;
            timer.Tick += HandlePositionChanged;

            Initializer(true);
        }

        #endregion

        #region Properties

        #region MediaElement and Display

        /// <summary>
        /// MediaElement
        /// </summary>
        public MediaElement MediaElement
        {
            get { return mediaElement; }
            set
            {
                mediaElement = value;
                base.RaisePropertyChanged("MediaElement");
            }
        }

        /// <summary>
        /// Get the Media original path.
        /// </summary>
        public string MediaPath
        {
            get;
            private set;
        }

        /// <summary>
        /// Total time of the media
        /// </summary>
        public string TotalTime
        {
            get { return totalTime; }
            set
            {
                if (value == totalTime)
                    return;
                totalTime = value;
                base.RaisePropertyChanged("TotalTime");
            }
        }

        /// <summary>
        /// The current play time
        /// </summary>
        public string CurrentTime
        {
            get { return currentTime; }
            set
            {
                currentTime = value;
                base.RaisePropertyChanged("CurrentTime");
            }
        }

        /// <summary>
        /// The Total seconds of media
        /// </summary>
        public double TotalSeconds
        {
            get { return totalSeconds; }
            set
            {
                totalSeconds = value;
                base.RaisePropertyChanged("TotalSeconds");
            }
        }

        /// <summary>
        /// The current played seconds
        /// </summary>
        public double CurrentValue
        {
            get { return currentValue; }
            set
            {
                currentValue = value;
                base.RaisePropertyChanged("CurrentValue");
            }
        }

        /// <summary>
        /// Volume
        /// </summary>
        public double Volume
        {
            get { return volume; }
            set
            {
                volume = value;
                mediaElement.Volume = value;
            }
        }

        public bool Mute
        {
            get { return mute; }
            set
            {
                mute = value;
                mediaElement.IsMuted = !mute;
                base.RaisePropertyChanged("Mute");
            }
        }

        #endregion

        #region Control Command

        /// <summary>
        /// Control whether is play or pause.
        /// </summary>
        public bool IsPlay
        {
            get { return isPlay; }
            set
            {
                isPlay = value;
                base.RaisePropertyChanged("IsPlay");
            }
        }

        /// <summary>
        /// Control the media state.
        /// </summary>
        private MediaState MediaState
        {
            get { return mediaState; }
            set
            {
                mediaState = value;
                switch (mediaState)
                {
                    case MediaState.Pause:
                        mediaElement.Pause();
                        IsPlay = true;
                        break;
                    case MediaState.Play:
                        mediaElement.Play();
                        IsPlay = false;
                        break;
                    case MediaState.Manual:
                    case MediaState.Stop:
                        Initializer(false);
                        mediaElement.Stop();
                        timer.Stop();
                        IsPlay = true;
                        break;
                    default:
                        break;
                }
            }
        }

        /// <summary>
        /// The Command to control play/pause
        /// </summary>
        public ICommand PlayPauseCommand
        {
            get
            {
                return new RelayCommand(
                    () =>
                    {
                        if (!isLoaded)
                        {
                            IsPlay = true;
                            return;
                        }

                        StartPosition = mediaElement.Position;
                        ShowStart = true;
                        switch (MediaState)
                        {
                            case MediaState.Play:
                                MediaState = MediaState.Pause;
                                break;
                            case MediaState.Pause:
                                MediaState = MediaState.Play;
                                ShowEnd = false;
                                break;
                            case MediaState.Manual:
                            case MediaState.Stop:
                                MediaState = MediaState.Play;
                                timer.Start();
                                break;
                            default:
                                break;
                        }
                    });
            }
        }

        /// <summary>
        /// The command to stop the media
        /// </summary>
        public ICommand StopCommand
        {
            get
            {
                return new RelayCommand(
                    () =>
                    {
                        if (mediaState == MediaState.Pause || mediaState == MediaState.Play)
                        {
                            MediaState = MediaState.Stop;
                        }
                    });
                    //() => { return (mediaState == MediaState.Pause || mediaState == MediaState.Play); });
            }
        }

        public ICommand LoadCommand
        {
            get
            {
                return new RelayCommand(() =>
                {
                    OpenFileDialog dlg = new OpenFileDialog();
                    dlg.Filter = "All Support Files(*.mp3;*.wma;*.wmv;*.avi;*.mp4) | *.mp3;*.wma;*.wmv;*.avi;*.mp4";

                    if (dlg.ShowDialog() == true)
                    {
                        LoadMedia(dlg.FileName);
                    }
                });
            }
        }

        #endregion

        #region Repeat

        /// <summary>
        /// The timespan of the start point of rereading.
        /// </summary>
        public TimeSpan StartPosition
        {
            get { return startPosition; }
            set
            {
                startPosition = value;
                base.RaisePropertyChanged("StartPosition");
            }
        }

        /// <summary>
        /// To show start label
        /// </summary>
        public bool ShowStart
        {
            get { return showStart; }
            set
            {
                if (showStart == value)
                    return;
                showStart = value;
                base.RaisePropertyChanged("ShowStart");
            }
        }

        /// <summary>
        /// The timespan of the end point of rereading.
        /// </summary>
        public TimeSpan EndPosition
        {
            get { return endPosition; }
            set
            {
                endPosition = value;
                base.RaisePropertyChanged("EndPosition");
            }
        }

        /// <summary>
        /// To show end label
        /// </summary>
        public bool ShowEnd
        {
            get { return showEnd; }
            set
            {
                if (showEnd == value)
                    return;
                showEnd = value;
                base.RaisePropertyChanged("ShowEnd");
            }
        }

        /// <summary>
        /// The command to set the start point of rereading
        /// </summary>
        public ICommand StartPointCommand
        {
            get
            {
                return new RelayCommand(() =>
                {
                    if (ShowEnd)
                        return;

                    StartPosition = mediaElement.Position;
                    ShowStart = true;
                });
            }
        }

        /// <summary>
        /// The command to set the end point of rereading.
        /// </summary>
        public ICommand EndPointCommand
        {
            get
            {
                return new RelayCommand(() =>
                {
                    if (!isLoaded || !ShowStart || StartPosition == mediaElement.Position)
                        return;

                    if (ShowEnd)
                    {
                        // Repeat
                        mediaElement.Position = StartPosition;
                    }
                    else
                    {
                        EndPosition = (MediaState == MediaState.Stop) ? mediaElement.NaturalDuration.TimeSpan : mediaElement.Position;
                        mediaElement.Position = StartPosition;
                        ShowEnd = true;
                    }
                    MediaState = MediaState.Play;
                    if (!timer.IsEnabled) timer.Start();
                });
            }
        }

        public ICommand ContinueCommand
        {
            get
            {
                return new RelayCommand(() =>
                {
                    if (!isLoaded)
                        return;

                    StartPosition = mediaElement.Position;
                    MediaState = MediaState.Play;
                    ShowEnd = false;
                });
            }
        }

        #endregion

        #endregion //Properties

        public event Action<bool> Loaded;

        #region Methods

        public void LoadMedia(string fileName)
        {
            Initializer(true);
            MediaPath = fileName;
            MediaState = MediaState.Manual;

            if (string.IsNullOrWhiteSpace(fileName) || !File.Exists(fileName))
            {
                OnLoaded(false);
                MessageBox.Show(string.Format("Could not find the file : {0}", fileName), "DictationCool", MessageBoxButton.OK, MessageBoxImage.Warning);
            }
            else
            {
                mediaElement.Source = new Uri(fileName);
                OnLoaded(true);
            }
        }

        public void HandleSeeking(double x, double width)
        {
            seeking = true;
            TimeSpan time = TimeSpan.FromSeconds(x * TotalSeconds / width);
            CurrentValue = time.TotalSeconds;
            CurrentTime = SetCurrentTime(time);
        }

        public void HandleSeekTo(double x, double width)
        {
            seeking = false;
            mediaElement.Position = TimeSpan.FromSeconds(x * TotalSeconds / width);
            StartPosition = mediaElement.Position;

            if (ShowEnd && EndPosition <= StartPosition)
                ContinueCommand.Execute(null);
        }

        private void Initializer(bool reset)
        {
            if (reset)
            {
                mediaElement.Source = null;
                isLoaded = false;
                TotalTime = "00:00";
            }

            mediaElement.Position = new TimeSpan();
            CurrentTime = "00:00";
            TotalTime = TotalTime;
            CurrentValue = 0;
            ShowStart = false;
            ShowEnd = false;
        }

        private void HandleMediaOpened(object sender, System.Windows.RoutedEventArgs e)
        {
            TotalTime = SetCurrentTime(mediaElement.NaturalDuration.TimeSpan);

            TotalSeconds = mediaElement.NaturalDuration.TimeSpan.TotalSeconds;

            //start to play
            isLoaded = true;
            timer.Start();
            //PlayPauseCommand.Execute(null);
        }

        private void HandleMediaEnded(object sender, RoutedEventArgs e)
        {
            MediaState = MediaState.Stop;
            ShowStart = true;
        }

        private void HandleMediaFailed(object sender, System.Windows.ExceptionRoutedEventArgs e)
        {
            MediaPath = string.Empty;
            OnLoaded(false);
        }

        private void HandlePositionChanged(object sender, EventArgs e)
        {
            if (seeking)
                return;

            CurrentTime = SetCurrentTime(mediaElement.Position);
            CurrentValue = mediaElement.Position.TotalSeconds;

            if (ShowEnd && mediaElement.Position.TotalMilliseconds >= EndPosition.TotalMilliseconds)
            {
                MediaState = MediaState.Pause;
            }
        }

        private string SetCurrentTime(TimeSpan time)
        {
            return (mediaElement.NaturalDuration.TimeSpan.TotalHours > 1) ? time.ToString(@"hh\:mm\:ss") : time.ToString(@"mm\:ss");
        }

        private void OnLoaded(bool success)
        {
            var handler = this.Loaded;
            if (handler != null)
            {
                handler(success);
            }
        }

        #endregion

        #region Fields

        private bool isLoaded;
        private bool isPlay = true;
        private bool showStart;
        private bool showEnd;
        private bool seeking;
        private double totalSeconds;
        private double currentValue;
        private double volume = 0.5;
        private string totalTime;
        private string currentTime;
        private MediaElement mediaElement = new MediaElement();
        private MediaState mediaState;
        private TimeSpan startPosition;
        private TimeSpan endPosition;
        private DispatcherTimer timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(1) };
        private bool mute = true;

        #endregion
    }
}